/* 
 * MCore Extensions for TTBlue
 * Creates a wrapper for TTAudioObjects that can be used to build an audio processing graph.
 * Copyright © 2008, Timothy Place
 * 
 * License: This code is licensed under the terms of the GNU LGPL
 * http://www.gnu.org/licenses/lgpl.html 
 */

#include "multicore.h"
#define thisTTClass MCoreObject


MCoreObject::MCoreObject(TTValue& arguments)
	: TTObject(TT("multicore.object"), arguments), flags(kMCoreProcessor), 
	inputs(NULL), outputs(NULL), audioObject(NULL)
{
	TTErr				err;
	TTSymbolPtr			wrappedObjectName;
	TTUInt16			initialNumChannels;
	TTAudioSignalPtr	sig = NULL;
	
	TT_ASSERT(multicore_correct_instantiation_args, arguments.getSize() == 2);
	
	arguments.get(0, &wrappedObjectName);
	arguments.get(1, initialNumChannels);
	inChansToOutChansRatio[0] = 1;
	inChansToOutChansRatio[1] = 1;
	
	err = TTObjectInstantiate(wrappedObjectName, &audioObject, initialNumChannels);

	numInlets = 1;
	err = TTObjectInstantiate(kTTSym_audiosignalarray, (TTObjectPtr*)&inputs, numInlets);
	//inputs = new TTAudioSignalArray(numInlets);
	inlets = new MCoreInlet[numInlets];
	inlets[0].setOwner(this, inputs, 0);
	
	err = TTObjectInstantiate(kTTSym_audiosignalarray, (TTObjectPtr*)&outputs, kTTVal1);
	//outputs = new TTAudioSignalArray(1);
	err = TTObjectInstantiate(kTTSym_audiosignal, &sig, initialNumChannels * TTLimitMin<TTFloat32>(inChansToOutChansRatio[1] / inChansToOutChansRatio[0], 1));
	outputs->setSignal(0, sig);
	sig = NULL;
	
	registerMessageWithArgument(objectFreeing);	// called when one of our input source objects is deleted
}


MCoreObject::~MCoreObject()
{
	delete[] inlets;
	TTObjectRelease(&audioObject);
	inputs->releaseAll();
	TTObjectRelease((TTObjectPtr*)&inputs);
	outputs->releaseAll();
	TTObjectRelease((TTObjectPtr*)&outputs);
}


TTErr MCoreObject::objectFreeing(const TTValue& theObjectBeingDeleted)
{
	TTObjectPtr o = theObjectBeingDeleted;
	TTBoolean	oldValid = valid;
	// This is an ugly operation
	// the problem is that we don't want to traverse a linked-list in the processing chain
	
	valid = false;
	while(getlock())
		;
	
	for(TTUInt16 i=0; i<numInlets; i++)
		inlets[i].drop(o);
	
	valid = oldValid;
	return kTTErrNone;
}


TTErr MCoreObject::setAudioOutputPtr(TTAudioSignalArrayPtr newOutputs)
{
	TTObjectPtr oldOutputs = outputs;
	
	outputs = (TTAudioSignalArrayPtr)TTObjectReference(newOutputs);

	if(oldOutputs)
		TTObjectRelease(&oldOutputs);
	return kTTErrNone;
}


TTErr MCoreObject::setInChansToOutChansRatio(TTUInt16 numInputChannels, TTUInt16 numOutputChannels)
{
	inChansToOutChansRatio[0] = numInputChannels;
	inChansToOutChansRatio[1] = numOutputChannels;
	return kTTErrNone;
}


TTErr MCoreObject::resetSources(TTUInt16 vs)
{
	// go through all of the sources and tell them we don't want to watch them any more
	for(TTUInt16 i=0; i<numInlets; i++)
		inlets[i].reset();
		
	// Generators will not receive an 'addSource' call, 
	// so we set them with the 'default' vector size provided by the global reset
	if(flags & kMCoreGenerator)
		outputs->allocAllWithVectorSize(vs);
	
	return kTTErrNone;
}


TTErr MCoreObject::addSource(MCoreObjectPtr anObject, TTUInt16 sourceOutletNumber, TTUInt16 anInletNumber)
{	
	return inlets[anInletNumber].addSource(anObject, sourceOutletNumber);
}


TTErr MCoreObject::init()
{
	TTUInt16	weDeliverNumChannels = outputs->getSignal(0).getNumChannels();
	TTUInt16	sourceProducesNumChannels = 0;
	
	lock();
	
	// init objects higher up in the chain first
	for(TTUInt16 i=0; i<numInlets; i++)
		inlets[i].init();
	
//#error this makes no sense -- sourceProducesNumChannels is always zero, so weDeliverNumChannels is always zero	
//	weDeliverNumChannels = sourceProducesNumChannels * TTLimitMin<TTFloat32>(inChansToOutChansRatio[1] / inChansToOutChansRatio[0], 1);
//	outputs->setAllMaxNumChannels(weDeliverNumChannels);
//	outputs->setAllNumChannels(weDeliverNumChannels);
	
	for(TTUInt16 i=0; i<numInlets; i++){
		TTUInt16 n = inlets[i].getMaxNumSourceChannels(weDeliverNumChannels);

		if(n>sourceProducesNumChannels)
			sourceProducesNumChannels = n;
	}

	// for generators, these are already alloc'd in the reset method
	if(flags & kMCoreGenerator)
		;
	else{
		outputs->setAllMaxNumChannels(weDeliverNumChannels);
		outputs->setAllNumChannels(weDeliverNumChannels);
		inputs->allocAllWithVectorSize(inlets[0].audioSources[0]->outputs->getVectorSize());
		outputs->allocAllWithVectorSize(inlets[0].audioSources[0]->outputs->getVectorSize());
	}

	// Even more ambiguous, what do we do for the acual audio object?  
	// For now we are setting it to the higher of the two options to be safe.
	// (and we are not taking sidechains into account at all
	if(weDeliverNumChannels > sourceProducesNumChannels)
		audioObject->setMaxNumChannels(weDeliverNumChannels);
	else
		audioObject->setMaxNumChannels(sourceProducesNumChannels);

	unlock();
	return kTTErrNone;
}


TTErr MCoreObject::prepareToProcess()
{
	lock();
	if(valid){
		processStatus = kProcessNotStarted;
		
		for(TTUInt16 i=0; i<numInlets; i++)
			inlets[i].prepareToProcess();
	}
	unlock();
	return kTTErrNone;
}


TTErr MCoreObject::getAudioOutput(TTAudioSignalPtr& returnedSignal, TTUInt16 signalIndex)
{
	lock();
	switch(processStatus){
		
		// we have not processed anything yet, so let's get started
		case kProcessNotStarted:
			processStatus = kProcessingCurrently;
			
			// zero the samples
			inputs->clearAll();

			// for each inlet...
			for(TTUInt16 i=0; i<numInlets; i++)
				inlets[i].pull();
			
			audioObject->process(inputs, outputs);
			
			// return
			returnedSignal = &outputs->getSignal(signalIndex);			
			processStatus = kProcessComplete;
			break;
		
		// we already processed everything that needs to be processed, so just set the pointer
		case kProcessComplete:
			returnedSignal = &outputs->getSignal(signalIndex);
			break;
		
		// to prevent feedback / infinite loops, we just hand back the last calculated output here
		case kProcessingCurrently:
			returnedSignal = &outputs->getSignal(signalIndex);
			break;
		
		// we should never get here
		default:
			unlock();
			return kTTErrGeneric;
	}
	unlock();
	return kTTErrNone;
}


TTUInt16 MCoreObject::getNumOutputChannels(TTUInt16 signalIndex)
{
	return outputs->getSignal(signalIndex).getNumChannels();
}


TTUInt16 MCoreObject::getSampleRate()
{
	TTUInt16 sr;
	audioObject->getAttributeValue(kTTSym_sr, sr);
	return sr;
}


TTUInt16 MCoreObject::getOutputVectorSize(TTUInt16 signalIndex)
{
	return outputs->getSignal(signalIndex).getVectorSize();
}


TTErr MCoreObject::addFlag(MCoreFlags newFlag)
{
	flags = newFlag;
	return kTTErrNone;
}

